#include "Win_Setting.h"
#include <wx/wx.h>
#include "Common.h"

static int standardSampleRates[] = {
    16000, 24000, 32000,
    44100, 48000, 96000, 192000, -1
};



static void PrintSupportedStandardSampleRates(
    const PaStreamParameters *inputParameters,
    const PaStreamParameters *outputParameters)
{
    // static double standardSampleRates[] = {
    //     8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
    //     44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated  list */
    // };
    int i, printCount;
    PaError err;

    printCount = 0;
    for (i = 0; standardSampleRates[i] > 0; i++)
    {
        err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
        if (err == paFormatIsSupported)
        {
            if (printCount == 0)
            {
                DEBUG_PRINT("\t%d", standardSampleRates[i]);
                printCount = 1;
            }
            else if (printCount == 4)
            {
                DEBUG_PRINT(",\n\t%d", standardSampleRates[i]);
                printCount = 1;
            }
            else
            {
                DEBUG_PRINT(", %d", standardSampleRates[i]);
                ++printCount;
            }
        }
    }
    if (!printCount)
        DEBUG_PRINT("None\n");
    else
        DEBUG_PRINT("\n");
}

static void getSupportedStandardSampleRates(
    const PaStreamParameters *inputParameters,
    const PaStreamParameters *outputParameters,
    wxStringList &strList)
{
    int i;
    PaError err;
    strList.Clear();
    for (i = 0; standardSampleRates[i] > 0; i++)
    {
        err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
        if (err == paFormatIsSupported)
        {
            strList.Add(wxString::Format("%d", standardSampleRates[i]));
        }
    }
}

int FindAudioDevice()
{
    int     i, numDevices, defaultDisplayed;
    const   PaDeviceInfo *deviceInfo;
    PaStreamParameters inputParameters, outputParameters;
    PaError err;

    numDevices = Pa_GetDeviceCount();
    if( numDevices < 0 )
    {
        DEBUG_PRINT( "ERROR: Pa_GetDeviceCount returned 0x%x\n", numDevices );
        err = numDevices;
        goto error;
    }

    DEBUG_PRINT( "Number of devices = %d\n", numDevices );
    for( i=0; i<numDevices; i++ )
    {
        deviceInfo = Pa_GetDeviceInfo( i );
        DEBUG_PRINT( "--------------------------------------- device #%d\n", i );

        /* Mark global and API specific default devices */
        defaultDisplayed = 0;
        if( i == Pa_GetDefaultInputDevice() )
        {
            DEBUG_PRINT( "[ Default Input" );
            defaultDisplayed = 1;
        }
        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultInputDevice )
        {
            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
            DEBUG_PRINT( "[ Default %s Input", hostInfo->name );
            defaultDisplayed = 1;
        }

        if( i == Pa_GetDefaultOutputDevice() )
        {
            DEBUG_PRINT( (defaultDisplayed ? "," : "[") );
            DEBUG_PRINT( " Default Output" );
            defaultDisplayed = 1;
        }
        else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice )
        {
            const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
            DEBUG_PRINT( (defaultDisplayed ? "," : "[") );
            DEBUG_PRINT( " Default %s Output", hostInfo->name );
            defaultDisplayed = 1;
        }

        if( defaultDisplayed )
            DEBUG_PRINT( " ]\n" );

        /* print device info fields */
#ifdef WIN32
        {   /* Use wide char on windows, so we can show UTF-8 encoded device names */
            // wchar_t wideName[MAX_PATH];
            // MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH-1);
            // wprintf( L"Name                        = %s\n", wideName );
            DEBUG_PRINT("Name                        = %s\n", deviceInfo->name);
        }
#else
        DEBUG_PRINT( "Name                        = %s\n", deviceInfo->name );
#endif
        DEBUG_PRINT( "Host API                    = %s\n",  Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
        DEBUG_PRINT( "Max inputs = %d", deviceInfo->maxInputChannels  );
        DEBUG_PRINT( ", Max outputs = %d\n", deviceInfo->maxOutputChannels  );

        DEBUG_PRINT( "Default low input latency   = %8.4f\n", deviceInfo->defaultLowInputLatency  );
        DEBUG_PRINT( "Default low output latency  = %8.4f\n", deviceInfo->defaultLowOutputLatency  );
        DEBUG_PRINT( "Default high input latency  = %8.4f\n", deviceInfo->defaultHighInputLatency  );
        DEBUG_PRINT( "Default high output latency = %8.4f\n", deviceInfo->defaultHighOutputLatency  );

#ifdef WIN32
#if PA_USE_ASIO
/* ASIO specific latency information */
        if( Pa_GetHostApiInfo( deviceInfo->hostApi )->type == paASIO ){
            long minLatency, maxLatency, preferredLatency, granularity;

            err = PaAsio_GetAvailableLatencyValues( i,
                    &minLatency, &maxLatency, &preferredLatency, &granularity );

            DEBUG_PRINT( "ASIO minimum buffer size    = %ld\n", minLatency  );
            DEBUG_PRINT( "ASIO maximum buffer size    = %ld\n", maxLatency  );
            DEBUG_PRINT( "ASIO preferred buffer size  = %ld\n", preferredLatency  );

            if( granularity == -1 )
                DEBUG_PRINT( "ASIO buffer granularity     = power of 2\n" );
            else
                DEBUG_PRINT( "ASIO buffer granularity     = %ld\n", granularity  );
        }
#endif /* PA_USE_ASIO */
#endif /* WIN32 */

        DEBUG_PRINT( "Default sample rate         = %8.2f\n", deviceInfo->defaultSampleRate );

    /* poll for standard sample rates */
        inputParameters.device = i;
        inputParameters.channelCount = deviceInfo->maxInputChannels;
        inputParameters.sampleFormat = paInt16;
        inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
        inputParameters.hostApiSpecificStreamInfo = NULL;

        outputParameters.device = i;
        outputParameters.channelCount = deviceInfo->maxOutputChannels;
        outputParameters.sampleFormat = paInt16;
        outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
        outputParameters.hostApiSpecificStreamInfo = NULL;

        if( inputParameters.channelCount > 0 )
        {
            DEBUG_PRINT("Supported standard sample rates\n for half-duplex 16 bit %d channel input = \n",
                    inputParameters.channelCount );
            PrintSupportedStandardSampleRates( &inputParameters, NULL );
        }

        if( outputParameters.channelCount > 0 )
        {
            DEBUG_PRINT("Supported standard sample rates\n for half-duplex 16 bit %d channel output = \n",
                    outputParameters.channelCount );
            PrintSupportedStandardSampleRates( NULL, &outputParameters );
        }

        if( inputParameters.channelCount > 0 && outputParameters.channelCount > 0 )
        {
            DEBUG_PRINT("Supported standard sample rates\n for full-duplex 16 bit %d channel input, %d channel output = \n",
                    inputParameters.channelCount, outputParameters.channelCount );
            PrintSupportedStandardSampleRates( &inputParameters, &outputParameters );
        }
    }

    // Pa_Terminate();

    DEBUG_PRINT("----------------------------------------------\n");
    return 1;

error:
    // Pa_Terminate();
    DEBUG_FPRINT( stderr, "Error number: %d\n", err );
    DEBUG_FPRINT( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );

    return 0;
}

Win_Setting::Win_Setting(wxWindow *parent,
            wxWindowID id,
            const wxString& title)
    : wxDialog(parent, id, title,wxDefaultPosition,wxDefaultSize,wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
    wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);

    wxTreebook *tree = new wxTreebook(this, wxID_ANY, wxDefaultPosition, wxSize(180, -1), wxTR_HAS_BUTTONS);

    // 右侧设置面板容器
    audioPanel = CreateAudioPanel(tree);
    displayPanel = CreateDisplayPanel(tree);
    networkPanel = CreateNetworkPanel(tree);
    tree->AddPage(audioPanel, wxT("音频设置"), false);
    tree->AddPage(displayPanel, wxT("显示设置"), false);
    tree->AddPage(networkPanel, wxT("网络设置"), false);
    // 取消和确认按钮
    wxButton* cancelButton = new wxButton(this, wxID_CANCEL, wxT("取消"));
    wxButton* okButton = new wxButton(this, wxID_OK, wxT("确定"));
    btnSizer->Add(cancelButton, 0, wxALL | wxCENTER, 10);
    btnSizer->Add(okButton, 0, wxALL | wxCENTER, 10);
    // 主布局添加控件
    mainSizer->Add(tree, 4, wxALL | wxEXPAND, 10);
    mainSizer->Add(btnSizer, 1, wxALL | wxCENTER | wxALIGN_RIGHT, 10);

    SetSizerAndFit(mainSizer);
    SetSize(wxSize(600, 400));

    hostApiType = paWDMKS;
    if(audioDevicetype)
    {
        for(int i=0; i<audioDevicetype->GetPageCount(); i++)
        {
            wxPanel *panelPage = (wxPanel*)audioDevicetype->GetPage(i);
            const PaHostApiInfo *hostInfo = (const PaHostApiInfo *)panelPage->GetClientData();
            if(hostInfo && hostInfo->type == hostApiType)
            {
                audioDevicetype->SetSelection(i);
                break;
            }
        }
    }

    // FindAudioDevice();
}

wxPanel* Win_Setting::CreateAudioPanel(wxWindow* parent)
{
    PaStreamParameters devParameters;
    wxPanel* panel = new wxPanel(parent);
    wxBoxSizer* sizerMain = new wxBoxSizer(wxHORIZONTAL);
    audioDevicetype = new wxChoicebook(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize);
    sizerMain->Add(new wxStaticText(panel, wxID_ANY, wxT("音频系统：")), 0, wxALL | wxALIGN_LEFT , 10);
    sizerMain->Add(audioDevicetype, 2, wxALL | wxALIGN_LEFT , 10);
    audioDevicetype->Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, &Win_Setting::OnChoiceChangedAudioSystem, this);
    audio_device_choice.clear();
    PaHostApiIndex hostCount = Pa_GetHostApiCount();
    for(int i = 0; i < hostCount; i++)
    {
        const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo(i);
        wxPanel *panelPage = new wxPanel(audioDevicetype, wxID_ANY);
        panelPage->SetClientData((void*)hostInfo);
        audioDevicetype->AddPage(panelPage, wxString::FromUTF8(hostInfo->name));
        wxStaticBox* boxInDev = new wxStaticBox(panelPage, wxID_ANY, wxT("输入设备"));
        wxStaticBox* boxOutDev = new wxStaticBox(panelPage, wxID_ANY, wxT("输出设备"));
        wxChoice *inputChoice = nullptr;
        wxChoice *outputChoice = nullptr;
        wxChoice *inputDevSamplerate = nullptr;
        wxChoice *outputDevSamplerate = nullptr;
        wxChoice *inputDevBit = nullptr;
        wxChoice *outputDevBit = nullptr;
        for(int n = 0; n < hostInfo->deviceCount; n++)
        {
            PaDeviceIndex deviceIndex = Pa_HostApiDeviceIndexToDeviceIndex(i, n);
            const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(deviceIndex);
            bool def = false;
            if(deviceInfo->hostApi == i)
            {
                wxString deviceName = wxString::FromUTF8(deviceInfo->name);
                if((deviceInfo->maxInputChannels > 0) && (deviceInfo->maxOutputChannels > 0))
                {
                    wxLogWarning(wxT("未处理同时支持输入和输出"), deviceName);
                    break;
                }
                else if(deviceInfo->maxInputChannels > 0)
                {
                    def = false;
                    if(inputChoice == nullptr)
                    {
                        inputChoice = new wxChoice(boxInDev, wxID_ANY, wxDefaultPosition, wxDefaultSize);
                        inputDevSamplerate = new wxChoice(boxInDev, wxID_ANY, wxDefaultPosition, wxDefaultSize);
                        inputDevBit = new wxChoice(boxInDev, wxID_ANY, wxDefaultPosition, wxDefaultSize);
                    }
                    if((deviceIndex == Pa_GetDefaultInputDevice()) || (Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultInputDevice == deviceIndex))
                    {
                        deviceName += wxT(" (默认)");
                        def = true;
                    }
                    inputChoice->Append(deviceName);
                    inputChoice->SetClientData(inputChoice->GetCount()-1, (void*)deviceInfo);
                    if(def == true)
                    {
                        inputChoice->SetSelection(inputChoice->GetCount()-1);
                    }
                }
                else if(deviceInfo->maxOutputChannels > 0)
                {
                    def = false;
                    if(outputChoice == nullptr)
                    {
                        outputChoice = new wxChoice(boxOutDev, wxID_ANY, wxDefaultPosition, wxDefaultSize);
                        outputDevSamplerate = new wxChoice(boxOutDev, wxID_ANY, wxDefaultPosition, wxDefaultSize);
                        outputDevBit = new wxChoice(boxOutDev, wxID_ANY,wxDefaultPosition, wxDefaultSize);
                    }
                    if((deviceIndex == Pa_GetDefaultOutputDevice()) || (Pa_GetHostApiInfo(deviceInfo->hostApi)->defaultOutputDevice == deviceIndex))
                    {
                        deviceName += wxT(" (默认)");
                        def = true;
                    }
                    outputChoice->Append(deviceName);
                    outputChoice->SetClientData(outputChoice->GetCount()-1, (void*)deviceInfo);
                    if(def == true)
                    {
                        outputChoice->SetSelection(outputChoice->GetCount()-1);
                    }
                }
            }
        }
        wxGridBagSizer* sizerGridInDev = new wxGridBagSizer(3, 2);
        wxGridBagSizer* sizerGridOutDev = new wxGridBagSizer(3, 2);
        sizerGridInDev->Add(new wxStaticText(boxInDev, wxID_ANY, wxT("输入设备：")), wxGBPosition(0, 0), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
        sizerGridOutDev->Add(new wxStaticText(boxOutDev, wxID_ANY, wxT("输出设备：")), wxGBPosition(0, 0), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
        if(inputChoice != nullptr)
        {
            for (int j = 0; standardSampleRates[j] > 0; j++)
            {
                wxString rateStr = wxString::Format(wxT("%d"), standardSampleRates[j]);
                inputDevSamplerate->Append(rateStr);
            }
            inputDevBit->Append(wxT("16"));
            inputDevBit->Append(wxT("24"));
            inputDevBit->Append(wxT("32"));
            inputDevBit->Append(wxT("float"));
            sizerGridInDev->Add(inputChoice, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
            sizerGridInDev->Add(new wxStaticText(boxInDev, wxID_ANY, wxT("采样率：")),   wxGBPosition(1, 0), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
            sizerGridInDev->Add(inputDevSamplerate, wxGBPosition(1, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
            sizerGridInDev->Add(new wxStaticText(boxInDev, wxID_ANY, wxT("位数：")),   wxGBPosition(2, 0), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
            sizerGridInDev->Add(inputDevBit, wxGBPosition(2, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
            inputChoice->Bind(wxEVT_CHOICE, &Win_Setting::OnChoiceChangedAudioDevice, this);
            if(inputChoice->GetSelection() == wxNOT_FOUND)
            {
                inputChoice->SetSelection(0);
            }
        }
        else
        {
            sizerGridInDev->Add(new wxStaticText(boxInDev, wxID_ANY, wxT("无输入设备")), wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
        }
        if(outputChoice != nullptr)
        {
            for (int j = 0; standardSampleRates[j] > 0; j++)
            {
                wxString rateStr = wxString::Format(wxT("%d"), standardSampleRates[j]);
                outputDevSamplerate->Append(rateStr);
            }
            outputDevBit->Append(wxT("16"));
            outputDevBit->Append(wxT("24"));
            outputDevBit->Append(wxT("32"));
            outputDevBit->Append(wxT("float"));
            sizerGridOutDev->Add(outputChoice, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
            sizerGridOutDev->Add(new wxStaticText(boxOutDev, wxID_ANY, wxT("采样率：")),   wxGBPosition(1, 0), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
            sizerGridOutDev->Add(outputDevSamplerate, wxGBPosition(1, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
            sizerGridOutDev->Add(new wxStaticText(boxOutDev, wxID_ANY, wxT("位数：")),   wxGBPosition(2, 0), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
            sizerGridOutDev->Add(outputDevBit, wxGBPosition(2, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
            outputChoice->Bind(wxEVT_CHOICE, &Win_Setting::OnChoiceChangedAudioDevice, this);
            if(outputChoice->GetSelection() == wxNOT_FOUND)
            {
                outputChoice->SetSelection(0);
            }
        }
        else
        {
            sizerGridOutDev->Add(new wxStaticText(boxOutDev, wxID_ANY, wxT("无输出设备")), wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL | wxCENTER | wxALIGN_CENTER_VERTICAL);
        }
        audio_device_choice.push_back({inputChoice,inputDevSamplerate,inputDevBit, outputChoice,outputDevSamplerate,outputDevBit});
        wxStaticBoxSizer* sizerSBoxInDev = new wxStaticBoxSizer(boxInDev, wxVERTICAL);
        wxStaticBoxSizer* sizerSBoxOutDev = new wxStaticBoxSizer(boxOutDev, wxVERTICAL);
        sizerSBoxInDev->Add(sizerGridInDev, 0, wxALL | wxEXPAND, 5);
        sizerSBoxOutDev->Add(sizerGridOutDev, 0, wxALL | wxEXPAND, 5);
        wxBoxSizer* sizerPanelMain = new wxBoxSizer(wxVERTICAL);
        sizerPanelMain->Add(sizerSBoxInDev, 0, wxALL | wxEXPAND, 5);
        sizerPanelMain->Add(sizerSBoxOutDev, 0, wxALL | wxEXPAND, 5);
        panelPage->SetSizer(sizerPanelMain);
    }
    panel->SetSizer(sizerMain);
    return panel;
}

wxPanel* Win_Setting::CreateDisplayPanel(wxWindow* parent)
{
    wxPanel* panel = new wxPanel(parent);
    wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("分辨率：")), 0, wxALL, 10);
    sizer->Add(new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                            wxArrayString({ wxT("1024x768"), wxT("1280x720"), wxT("1920x1080") })),
               0, wxEXPAND | wxLEFT | wxRIGHT, 10);
    panel->SetSizer(sizer);

    return panel;
}

wxPanel* Win_Setting::CreateNetworkPanel(wxWindow* parent)
{
    wxPanel* panel = new wxPanel(parent);
    wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
    sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("代理服务器：")), 0, wxALL, 10);
    sizer->Add(new wxTextCtrl(panel, wxID_ANY, wxT("http://proxy.example.com")), 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
    panel->SetSizer(sizer);
    return panel;
}

void Win_Setting::OnChoiceChangedAudioSystem(wxCommandEvent& event)
{
    wxChoicebook* choice = static_cast<wxChoicebook*>(event.GetEventObject());
    wxPanel *panelPage = (wxPanel*)choice->GetCurrentPage();
    const PaHostApiInfo *hostInfo = (const PaHostApiInfo *)panelPage->GetClientData();
    if(hostInfo)
    {
        hostApiType = hostInfo->type;
        DEBUG_PRINT(("选择的音频系统：%s\n"), hostInfo->name);
        pa_device_choice device_choice = audio_device_choice.at(choice->GetSelection());
        if(device_choice.in_dev_choice)
        {
            const PaDeviceInfo *deviceInfo = (const PaDeviceInfo*)device_choice.in_dev_choice->GetClientData(device_choice.in_dev_choice->GetSelection());
            DEBUG_PRINT(("选择的输入设备：%s\n"), deviceInfo->name);
        }
        else
        {
            DEBUG_PRINT(("没有输入设备\n"));
        }
        if(device_choice.out_dev_choice)
        {
            const PaDeviceInfo *deviceInfo = (const PaDeviceInfo*)device_choice.out_dev_choice->GetClientData(device_choice.out_dev_choice->GetSelection());
            DEBUG_PRINT(("选择的输出设备：%s\n"), deviceInfo->name);
        }
        else
        {
            DEBUG_PRINT(("没有输出设备\n"));
        }
    }
    else
    {
        DEBUG_PRINT(("未选择音频系统\n"));
    }
}

void Win_Setting::OnChoiceChangedAudioDevice(wxCommandEvent& event)
{
    wxChoice* choice = static_cast<wxChoice*>(event.GetEventObject());
    const PaDeviceInfo *deviceInfo = (const PaDeviceInfo*)choice->GetClientData(choice->GetSelection());
    if(deviceInfo)
    {
        DEBUG_PRINT(("选择的设备：%s\n"), deviceInfo->name);
    }
    else
    {
        DEBUG_PRINT(("未选择设备\n"));
    }
}
